package com.hbean.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import org.springframework.boot.configurationprocessor.json.JSONException;
import org.springframework.boot.configurationprocessor.json.JSONObject;
import org.springframework.util.DigestUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.security.SecureRandom;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Description: 工具类
 * @Project: hbean
 * @Author: hjibo
 * @CreateDate: 2018-09-28 15:47
 * @ModificationHistory:
 */
public class HUtil {

    /**
     * description: 工具类中的方法都是静态方式访问的因此将构造器私有不允许创建对象(绝对好习惯)
     * param :
     * create_user: hjibo
     * create_date: 2018-07-31
     * create_time: 14:40
     */
    private HUtil() {
        throw new AssertionError();
    }

    /*****************************************************null、"" 判断 操作*****************************************************/
    public static boolean isNotEmpty(Object obj) {
        /**
         * description: 判断对象是否为 null、""
         * param : [obj]
         * create_user: hjibo
         * create_date: 2018-06-06
         * create_time: 14:20
         */
        if (obj == null) {
            return false;
        } else if (obj.toString().trim().equals("")) {
            return false;
        } else if (obj.toString().trim().toLowerCase().equals("null")) {
            return false;
        }
        return true;
    }

    /*****************************************************字符串 操作*****************************************************/
    /**
     * description: 去掉字符串前面/后面的 0
     * param : [str]
     * create_user: hjibo
     * create_date: 2018-06-06
     * create_time: 14:24
     */
    public static String strReduce(String str,int i) {
        String newStr = "";
        switch (i){
            case 1:
                newStr = str.replaceAll("^(0+)", "");//去掉字符串前面的 0
                break;
            case 2:
                newStr = str.replaceAll("0*$", "");//去掉字符串后面的 0
                break;
        }
        return newStr;
    }
    /**
     * description: 判断字符串是否为纯数字字符串
     * param : [str]
     * create_user: hjibo
     * create_date: 2018-06-06
     * create_time: 14:24
     */
    public static boolean isNumberString(String str) {
        Pattern pattern = Pattern.compile("[+-]?[0-9]+[0-9]*(\\.[0-9]+)?");
        Matcher match = pattern.matcher(str);
        return match.matches();
    }
    /**
     * description: 字符串 转换成 list
     * param : [srcStr：源字符串, conStr：转换条件（比如以英文分号“;”或者逗号“,”分割的字符串）]
     * create_user: hjibo
     * create_date: 2018-06-06
     * create_time: 14:50
     */
    public static List<String> stringToList(String srcStr, String conStr) {
        return Arrays.asList(srcStr.split(conStr));
    }
    /**
     * description: jsonStr字符串 转换为  实体对象
     * param : [jsonStr, obj]
     * create_user: hjibo
     * create_date: 2018-06-07
     * create_time: 00:00
     */
    public static<T> Object jsonStrToJavaObject(String jsonStr,Class<T> obj) {
        T t = null;
        try{
            ObjectMapper objectMapper = new ObjectMapper();
            t = objectMapper.readValue(jsonStr,obj);
        }catch(Exception e) {
            e.printStackTrace();
        }
        return t;
    }
    /**
     * description: 从 JsonStr字符串中 取出 key为keyStr 的值
     * param : [jsonStr, keyStr]
     * create_user: hjibo
     * create_date: 2018-06-07
     * create_time: 00:04
     */
    public static String jsonStrToKeyValue(String jsonStr,String keyStr) {
        String result = null;
        try {
            JSONObject jsonObject  = new JSONObject(jsonStr);
            result = (String) jsonObject.get(keyStr);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return result;
    }
    /**
     * description: 将map转成json字符串
     * create_user: hjibo
     * create_date: 2018-11-03 17:56
     * param : [map]
     */
    public static String mapToJsonStr(Map map) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.writeValueAsString(map);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /*****************************************************uuid 操作*****************************************************/
    public static String uuidOf18() {
        /**
         * description: 根据当前系统时间获取 18位uuid（由日期字符串 + 4位随机数字组成）
         * param : []
         * create_user: hjibo
         * create_date: 2018-06-06
         * create_time: 15:38
         */
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        StringBuilder str = new StringBuilder();
        str.append(sdf.format(new Date()));
        for (int i = 0; i < 4; i++) {
            int j = new Random().nextInt(10);
            str.append("0123456789".charAt(j));
        }
        return str.toString();
    }
    public static String uuidOf36() {
        /**
         * description: 获取 36位uuid
         * param : []
         * create_user: hjibo
         * create_date: 2018-06-06
         * create_time: 16:11
         */
        StringBuilder str = new StringBuilder();
        str.append(UUID.randomUUID().toString().replace("-", ""));
        for (int i = 0; i < 4; i++) {
            int j = new Random().nextInt("0123456789abcdefghijkmlnopqrstuvwxyz".length());
            str.append("0123456789abcdefghijkmlnopqrstuvwxyz".charAt(j));
        }
        return str.toString();
    }

    /*****************************************************日期格式化 操作*****************************************************/
    /**
     * description: 将字符串型 转换成另一种格式的 字符串型
     * create_user: hjibo
     * create_date: 2018/10/12 14:07
     * param : dateStr：待格式化的日期字符串，srcFormatStr：待格式化日期字符串的格式，desFormatStr：待格式化的日期字符串要转换成的目标格式
     */
    public static String stringToDateStr(String dateStr, String srcFormatStr,String desFormatStr) {
        if(dateStr != null){
            return dateToString(stringToDate(dateStr,srcFormatStr),desFormatStr);
        }else{
            return null;
        }
    }

    public static String dateToString(Date date, String formatStr) {
        /**
         * description: 日期类型 转换成 字符串型
         * param : [date：日期, formatStr：转换格式（"yyyy-MM-dd HH:mm:ss"、"yyyy-MM-dd"、"yyyyMMddHHmmss" 、"yyyyMMdd" 等）]
         * create_user: hjibo
         * create_date: 2018-06-06
         * create_time: 17:13
         */
        SimpleDateFormat sdf = new SimpleDateFormat(formatStr);
        return sdf.format(date);
    }
    public static Date stringToDate(String dateStr, String formatStr) {
        /**
         * description: 字符串型 转换成 日期类型
         * param : [dateStr：日期字符串, formatStr：转换格式（"yyyy-MM-dd HH:mm:ss"、"yyyy-MM-dd"、"yyyyMMddHHmmss"）]
         * create_user: hjibo
         * create_date: 2018-06-06
         * create_time: 17:18
         */
        Date date = null;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat(formatStr);
            date = sdf.parse(dateStr);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return date;
    }

    /*****************************************************编码解码 操作*****************************************************/
    private static final String key = "hbean";//AES加密/解密 密钥
    private static final String slat = "ASDFGHJKL;'QWERTYUIOP[]ZXCVBNM,./)(*&^%$#@!=-_+asdfghjklqwertyuiopcxzvbnm";//MD5盐值字符串，用于混淆MD5

    public static String base64Encode(byte[] bytes) {
        /**
         * description: base64 编码：将二进制数据转换成ASCii字符（字符流）
         *                          用于传输8bit字节的编码方式；base64就是基于64个可打印字符来表示二进制数据的方法
         * param : [bytes]
         * create_user: hjibo
         * create_date: 2018-06-06
         * create_time: 23:17
         */
        return new BASE64Encoder().encode(bytes);
    }
    public static byte[] base64Decode(String base64Code){
        /**
         * description: base64 解码
         * param : [base64Code]
         * create_user: hjibo
         * create_date: 2018-06-06
         * create_time: 23:25
         */
        byte[] bytes = null;
        try {
            bytes = new BASE64Decoder().decodeBuffer(base64Code);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bytes;
    }
    public static String aesEncrypt(String srcStr) {
        /**
         * description: AES 加密
         * param : [content]
         * create_user: hjibo
         * create_date: 2018-06-06
         * create_time: 23:26
         */
        byte[] bytes = null;
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(key.getBytes());
            kgen.init(128, random);
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
            bytes = cipher.doFinal(srcStr.getBytes("utf-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return base64Encode(bytes);
    }
    public static String aesDecrypt(String encryptStr) {
        /**
         * description: AES 解密
         * param : [encryptStr]
         * create_user: hjibo
         * create_date: 2018-06-06
         * create_time: 23:32
         */
        String str = null;
        try {
            byte[] encryptBytes = base64Decode(encryptStr);
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            random.setSeed(key.getBytes());
            kgen.init(128, random);
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(kgen.generateKey().getEncoded(), "AES"));
            byte[] decryptBytes = cipher.doFinal(encryptBytes);
            str = new String(decryptBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }
    public static String md5Encrypt(String srcStr) {
        /**
         * description: MD5加密 （不可逆）
         * param : [srcStr]
         * create_user: hjibo
         * create_date: 2018-06-06
         * create_time: 23:34
         */
        String str = null;
        try {
            String baseStr = srcStr + "/" + slat;
            String md5Str = DigestUtils.md5DigestAsHex(baseStr.getBytes());
            str = base64Encode(md5Str.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return str;
    }

    /*****************************************************地址、文件、路径 操作*****************************************************/
    public static String ipNetAddressOfRemote(HttpServletRequest request) {
        /**
         * description: 从http请求中获取远程（客户端）ip地址
         * param : [request]
         * create_user: hjibo
         * create_date: 2018-06-07
         * create_time: 10:26
         */
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip.equals("0:0:0:0:0:0:0:1")) {
            ip = "127.0.0.1";
            return ip;
        }
        return ip;
    }
    public static String ipNetAddressOfLocal(HttpServletRequest request) {
        /**
         * description: 从http请求中获取本地（服务端）ip地址
         * param : [request]
         * create_user: hjibo
         * create_date: 2018-06-07
         * create_time: 10:27
         */
        return request.getLocalAddr();
    }
    public static String urlOfClassesRoot(){
        /**
         * description: 获取类加载的根路径 ： 如： /D:/workspaces/ideaProject/hbean/target/classes/
         * param : []
         * create_user: hjibo
         * create_date: 2018-06-07
         * create_time: 09:06
         */
        String path = HUtil.class.getResource("/").getPath();
        return path;
    }
    public static String fileCreate(String descfilePath){
        /**
         * description: 生成指定文件
         * param : [descfilePath：待生成文件]
         * create_user: hjibo
         * create_date: 2018-06-07
         * create_time: 11:08
         */
        File file = new File(descfilePath);
        try {
            // 创建文件
            if(!file.exists()){
                file.createNewFile();
                System.out.println("成功生成文件");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file.toString().replaceAll("\\\\","/");//将window中的文件路径转化为java中可识别的路径
    }
    public static String fileDelete(String srcfilePath){
        /**
         * description: 删除指定目录文件
         * param : [srcfilePath：文件地址]
         * create_user: hjibo
         * create_date: 2018-06-07
         * create_time: 11:05
         */
        File file = new File(srcfilePath);
        if(file.exists()){
            file.delete();
            System.out.println("成功删除文件");
        }else{
            System.out.println("目标文件不存在");
        }
        return file.toString().replaceAll("\\\\","/");//将window中的文件路径转化为java中可识别的路径
    }
    public static HttpServletResponse fileDownload(HttpServletResponse response, String srcFilePath){
        /**
         * description: web 文件下载
         * param : [response ：HttpServletResponse对象, srcFilePath ：源文件]
         * create_user: hjibo
         * create_date: 2018-06-07
         * create_time: 11:40
         */
        try {
            File file = new File(srcFilePath);//文件对象
            String filename = file.getName();//文件名

            //输入流（读到内存）
            InputStream fis = new BufferedInputStream(new FileInputStream(srcFilePath));
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            fis.close();

            // response 设置
            response.reset();
            response.addHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes("GBK"),"8859_1"));
            response.addHeader("Content-Length", "" + file.length());
            response.setContentType("application/octet-stream");

            //输出流（从内存写出）
            OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
            toClient.write(buffer);
            toClient.flush();
            toClient.close();

        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return response;
    }
    public static byte[] fileToBytes(String srcFilePath){
        /**
         * description: 将文件转化为二进制流
         * param : [srcFilePath]
         * create_user: hjibo
         * create_date: 2018-08-29
         * create_time: 15:04
         */
        InputStream in = null;
        byte[] data = null;
        try {
            in = new FileInputStream(srcFilePath);
            data = new byte[in.available()];
            in.read(data);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return data;
    }
    public static void createDocByTemplate(String temName,String temDirectory,Map dataMap,String descFilePath){
        /**
         * description: 利用 freemarker模板 生成 word文档
         * param : [temName ：模板文件名, temDirectory ：模板文件所在目录, dataMap ： 待填充的数据, descFilePath ：目标文件全路径]
         * create_user: hjibo
         * create_date: 2018-06-07
         * create_time: 11:30
         */
        try {
            //初始化配置文件
            Configuration configuration = new Configuration();
            configuration.setDefaultEncoding("utf-8");// 设置编码
//            configuration.setClassForTemplateLoading(HUtil.class,"/static/template");//获取模板所在目录
            configuration.setDirectoryForTemplateLoading(new File(temDirectory));//获取模板所在目录
            Template template = configuration.getTemplate(temName);//获取模板文件
            //获取目标文件的输出流，将输入写入模板
            FileOutputStream fos = new FileOutputStream(new File(descFilePath));
            Writer out = new BufferedWriter(new OutputStreamWriter(fos, "utf-8"),10240);
            try {
                template.process(dataMap,out);
            } catch (TemplateException e) {
                e.printStackTrace();
            } finally {
                if(out != null){
                    out.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /*****************************************************身份证号码 操作*****************************************************/
    /**
     * 获取 生日
     * @param strIdCard
     * @return
     */
    public static String idCardToBirthday(String strIdCard){
        Integer len = strIdCard.length();
        String bir="";
        if(idCardValid(strIdCard)){
            if(len==18){
                bir = getBirthdayByIdCard18(strIdCard);
            }else{
                bir = getBirthdayByIdCard15(strIdCard);
            }
        }else{
            System.out.println("身份证号码无效");
        }
        return bir;
    }
    /**
     * 获取 年龄
     * @param strIdCard
     * @return
     */
    public static String idCardToAge(String strIdCard){
        Integer len = strIdCard.length();
        String age="";
        if(idCardValid(strIdCard)){
            if(len==18){
                age = getAgeByIdCard18(strIdCard);
            }else{
                age = getAgeByIdCard15(strIdCard);
            }
        }else{
            System.out.println("身份证号码无效");
        }
        return age;
    }
    /**
     * 获取 性别
     * @param strIdCard
     * @return
     */
    public static String idCardToSex(String strIdCard){
        Integer len = strIdCard.length();
        String sex="";
        if(idCardValid(strIdCard)){
            if(len==18){
                sex = getSexByIdCard18(strIdCard);
            }else{
                sex = getSexByIdCard15(strIdCard);
            }
        }else{
            System.out.println("身份证号码无效");
        }
        return sex;
    }
    /**
     * 18位身份证
     * 获取 生日
     * @param strIdCard
     * @return
     */
    private static String getBirthdayByIdCard18(String strIdCard) {
        //身份证上的年月日
        String idyear = strIdCard.substring(6).substring(0, 4);// 得到年份
        String idyue = strIdCard.substring(10).substring(0, 2);// 得到月份
        String idday = strIdCard.substring(12).substring(0, 2);//得到日
        String bir = idyear+"-"+idyue +"-"+ idday;
        return bir;
    }
    /**
     * 18位身份证
     * 获取 性别
     * @param strIdCard
     * @return
     */
    private static String getSexByIdCard18(String strIdCard) {
        String sex = "2"; //未知
        if (Integer.parseInt(strIdCard.substring(16).substring(0, 1)) % 2 == 0) {// 判断性别
            sex = "0"; //女
        } else {
            sex = "1"; //男
        }
        return sex;
    }
    /**
     * 18位身份证
     * 获取 年龄
     * @param strIdCard
     * @return
     */
    private static String getAgeByIdCard18(String strIdCard){
        //身份证上的年月日
        String idyear = strIdCard.substring(6).substring(0, 4);// 得到年份
        String idyue = strIdCard.substring(10).substring(0, 2);// 得到月份
        String idday = strIdCard.substring(12).substring(0, 2);//得到日
        String idyr = idyue + idday + "";

        //当前年月日
        String year = new SimpleDateFormat("yyyy-MM-dd").format(new Date()).substring(0, 4);// 当前年份
        String yue = new SimpleDateFormat("yyyy-MM-dd").format(new Date()).substring(5, 7);// 月份
        String day = new SimpleDateFormat("yyyy-MM-dd").format(new Date()).substring(8, 10);
        String yr = yue + day + "";

        int age = 0;
        if (Integer.parseInt(idyr) <= Integer.parseInt(yr)) { // 表示生日已过
            age = Integer.parseInt(year) - Integer.parseInt(idyear) + 1;
        } else {// 生日未过
            age = Integer.parseInt(year) - Integer.parseInt(idyear);
        }
        return age + "";
    }
    /**
     * 15位身份证
     * 获取 生日
     * @param strIdCard
     * @return
     */
    private static String getBirthdayByIdCard15(String strIdCard) {
        //身份证上的年月日
        String idyear = "19" + strIdCard.substring(6, 8);
        String idyue = strIdCard.substring(8, 10);
        String idday = strIdCard.substring(10, 12);
        String bir = idyear+"-"+idyue +"-"+ idday;
        return bir;
    }
    /**
     * 15位身份证
     * 获取 性别
     * @param strIdCard
     * @return
     */
    private static String getSexByIdCard15(String strIdCard) {
        String sex = "2"; //未知
        if (Integer.parseInt(strIdCard.substring(14, 15)) % 2 == 0) {// 判断性别
            sex = "0"; //女
        } else {
            sex = "1"; //男
        }
        return sex;
    }
    /**
     * 15位身份证
     * 获取 年龄
     * @param strIdCard
     * @return
     */
    private static String getAgeByIdCard15(String strIdCard){
        //身份证上的年月日
        String idyear = "19" + strIdCard.substring(6, 8);
        String idyue = strIdCard.substring(8, 10);
        String idday = strIdCard.substring(10, 12);
        String idyr = idyue + idday + "";

        //当前年月日
        String year = new SimpleDateFormat("yyyy-MM-dd").format(new Date()).substring(0, 4);// 当前年份
        String yue = new SimpleDateFormat("yyyy-MM-dd").format(new Date()).substring(5, 7);// 月份
        String day = new SimpleDateFormat("yyyy-MM-dd").format(new Date()).substring(8, 10);
        String yr = yue + day + "";

        int age = 0;
        if (Integer.parseInt(idyr) <= Integer.parseInt(yr)) { // 表示生日已过
            age = Integer.parseInt(year) - Integer.parseInt(idyear) + 1;
        } else {// 生日未过
            age = Integer.parseInt(year) - Integer.parseInt(idyear);
        }
        return age + "";
    }
    public static boolean idCardValid(String strIdCard){
        /**
         * description: 校验 身份证号 有效性
         * param : [strIdCard]
         * create_user: hjibo
         * create_date: 2018-06-07
         * create_time: 15:46
         */
        String Ai = "";
        //判断号码的长度是否正确（应为15位或18位）
        if (strIdCard.length() != 15 && strIdCard.length() != 18) {
            System.out.println("身份证号码的长度不正确（应为15位或18位）");
            return false;
        }
        //18位身份证前17位位数字，如果是15位的身份证则所有号码都为数字
        if (strIdCard.length() == 18) {
            Ai = strIdCard.substring(0, 17);
        } else if (strIdCard.length() == 15) {
            Ai = strIdCard.substring(0, 6) + "19" + strIdCard.substring(6, 15);
        }
        if (isNumeric(Ai) == false) {
            System.out.println("15位身份证号码都应为数字 ; 18位号码除最后一位外，都应为数字");
            return false;
        }
        //判断出生年月是否有效
        String strYear = Ai.substring(6, 10);// 年份
        String strMonth = Ai.substring(10, 12);// 月份
        String strDay = Ai.substring(12, 14);// 日期
        if (isDate(strYear + "-" + strMonth + "-" + strDay) == false) {
            System.out.println("身份证出生日期无效");
            return false;
        }
        GregorianCalendar gc = new GregorianCalendar();
        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd");
        try {
            if ((gc.get(Calendar.YEAR) - Integer.parseInt(strYear)) > 150
                    || (gc.getTime().getTime() - s.parse(
                    strYear + "-" + strMonth + "-" + strDay).getTime()) < 0) {
                System.out.println("身份证生日不在有效范围");
                return false;
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        if (Integer.parseInt(strMonth) > 12 || Integer.parseInt(strMonth) == 0) {
            System.out.println("身份证月份无效");
            return false;
        }
        if (Integer.parseInt(strDay) > 31 || Integer.parseInt(strDay) == 0) {
            System.out.println("身份证日期无效");
            return false;
        }
        //判断地区码是否有效(如果身份证前两位的地区码不在Hashtable，则地区码有误)
        Hashtable areacode = GetAreaCode();
        if (areacode.get(Ai.substring(0, 2)) == null) {
            System.out.println("身份证地区编码无效");
            return false;
        }
        //判断校验码是否正确
        if(isVarifyCode(Ai,strIdCard)==false){
            System.out.println("身份证校验码无效(最后一位数字)");
            return false;
        }
        return true;
    }

    /*
     * 判断第18位校验码是否正确
     * 第18位校验码的计算方式：
       　　1. 对前17位数字本体码加权求和
       　　公式为：S = Sum(Ai * Wi), i = 0, ... , 16
       　　其中Ai表示第i个位置上的身份证号码数字值，Wi表示第i位置上的加权因子，其各位对应的值依次为： 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2
       　　2. 用11对计算结果取模
       　　Y = mod(S, 11)
       　　3. 根据模的值得到对应的校验码
       　　对应关系为：
       　　 Y值：     0  1  2  3  4  5  6  7  8  9  10
       　　校验码： 1  0  X  9  8  7  6  5  4  3   2
    */
    private static boolean isVarifyCode(String Ai,String IDStr) {
        String[] VarifyCode = { "1", "0", "X", "9", "8", "7", "6", "5", "4","3", "2" };
        String[] Wi = { "7", "9", "10", "5", "8", "4", "2", "1", "6", "3", "7","9", "10", "5", "8", "4", "2" };
        int sum = 0;
        for (int i = 0; i < 17; i++) {
            sum = sum + Integer.parseInt(String.valueOf(Ai.charAt(i))) * Integer.parseInt(Wi[i]);
        }
        int modValue = sum % 11;
        String strVerifyCode = VarifyCode[modValue];
        Ai = Ai + strVerifyCode;
        if (IDStr.length() == 18) {
            if (Ai.equals(IDStr) == false) {
                return false;
            }
        }
        return true;
    }
    /**
     * 将所有地址编码保存在一个Hashtable中
     * @return Hashtable 对象
     */
    private static Hashtable GetAreaCode() {
        Hashtable hashtable = new Hashtable();
        hashtable.put("11", "北京");
        hashtable.put("12", "天津");
        hashtable.put("13", "河北");
        hashtable.put("14", "山西");
        hashtable.put("15", "内蒙古");
        hashtable.put("21", "辽宁");
        hashtable.put("22", "吉林");
        hashtable.put("23", "黑龙江");
        hashtable.put("31", "上海");
        hashtable.put("32", "江苏");
        hashtable.put("33", "浙江");
        hashtable.put("34", "安徽");
        hashtable.put("35", "福建");
        hashtable.put("36", "江西");
        hashtable.put("37", "山东");
        hashtable.put("41", "河南");
        hashtable.put("42", "湖北");
        hashtable.put("43", "湖南");
        hashtable.put("44", "广东");
        hashtable.put("45", "广西");
        hashtable.put("46", "海南");
        hashtable.put("50", "重庆");
        hashtable.put("51", "四川");
        hashtable.put("52", "贵州");
        hashtable.put("53", "云南");
        hashtable.put("54", "西藏");
        hashtable.put("61", "陕西");
        hashtable.put("62", "甘肃");
        hashtable.put("63", "青海");
        hashtable.put("64", "宁夏");
        hashtable.put("65", "新疆");
        hashtable.put("71", "台湾");
        hashtable.put("81", "香港");
        hashtable.put("82", "澳门");
        hashtable.put("91", "国外");
        return hashtable;
    }
    /**
     * 判断字符串是否为数字,0-9重复0次或者多次
     * @param strnum
     * @return
     */
    private static boolean isNumeric(String strnum) {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(strnum);
        if (isNum.matches()) {
            return true;
        } else {
            return false;
        }
    }
    /**
     * 功能：判断字符串出生日期是否符合正则表达式：包括年月日，闰年、平年和每月31天、30天和闰月的28天或者29天
     *
     * @param
     * @return
     */
    private static boolean isDate(String strDate) {
        Pattern pattern = Pattern
                .compile("^((\\d{2}(([02468][048])|([13579][26]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))[\\-\\/\\s]?((((0?[13578])|(1[02]))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-\\/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-\\/\\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))?$");
        Matcher m = pattern.matcher(strDate);
        if (m.matches()) {
            return true;
        } else {
            return false;
        }
    }

    /*****************************************************根据当前日期获取周信息 操作*****************************************************/

    public static int weekOfDate(Date date) {
        /**
         * description: 根据指定时间获取其所在周
         * param : [date]
         * create_user: hjibo
         * create_date: 2018-07-14
         * create_time: 17:21
         */
        Calendar c = new GregorianCalendar();
        c.setFirstDayOfWeek(Calendar.MONDAY);//规定每周的第一天为 周一
        c.setMinimalDaysInFirstWeek(4);//根据中国标准，一年中的第一个日历星期包括该年的第一个星期四，那么一年中第一个周最少要有四天（即1月1日为周四）
        c.setTime(date);
        int weekNum = c.get(Calendar.WEEK_OF_YEAR);
        return weekNum;
    }

    public static Date weekLastDayOfDate(Date date) {
        /**
         * description: 根据指定时间获取其所在周的结束日期
         * param : [date]
         * create_user: hjibo
         * create_date: 2018-07-16
         * create_time: 09:08
         */
        Calendar c = new GregorianCalendar();
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.setTime(date);
        c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek() + 6); // Sunday 周日
        return c.getTime();
    }

    public static Date weekFirstDayOfDate(Date date) {
        /**
         * description: 根据指定时间获取其所在周的起始日期
         * param : [date]
         * create_user: hjibo
         * create_date: 2018-07-16
         * create_time: 09:08
         */
        Calendar c = new GregorianCalendar();
        c.setFirstDayOfWeek(Calendar.MONDAY);
        c.setTime(date);
        c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek()); // Monday 周一
        return c.getTime();
    }

    public static Date weekFirstDay(int week) {
        /**
         * description: 获取本年指定周的起始日期
         * param : [week]
         * create_user: hjibo
         * create_date: 2018-07-16
         * create_time: 14:34
         */
        int currentWeek = weekOfDate(new Date());//当前周
        Date date = weekLastDayOfDate(new Date());//当前周的最后一天

        //当前周最后一天的日历对象
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        //指定周最后一天的日历对象
        Calendar cal = (GregorianCalendar) calendar.clone();
        cal.add(Calendar.DATE, (week - currentWeek) * 7);

        return weekFirstDayOfDate(cal.getTime());
    }

    public static Date weekLastDay(int week) {
        /**
         * description: 获取本年指定周的结束日期
         * param : [week]
         * create_user: hjibo
         * create_date: 2018-07-16
         * create_time: 14:34
         */
        int currentWeek = weekOfDate(new Date());//当前周
        Date date = weekLastDayOfDate(new Date());//当前周的最后一天

        //当前周最后一天的日历对象
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        //指定周最后一天的日历对象
        Calendar cal = (GregorianCalendar) calendar.clone();
        cal.add(Calendar.DATE, (week - currentWeek) * 7);

        return weekLastDayOfDate(cal.getTime());
    }

    /*****************************************************数组、排序、查找 操作*****************************************************/

    public static int[] arrayOfRandom(int arrLength,int arrLimit,boolean boo){
        /**
         * description: 生成一个重复（不重复）的随机整型数组
         * param : [arrLength：数组长度, arrLimit：随机数最大值, boo：是否允许重复（true：允许，false：不允许）]
         * create_user: hjibo
         * create_date: 2018-08-01
         * create_time: 09:35
         */
        int[] v = new int[arrLength];//默认元素为 0
        boolean[] b = new boolean[arrLimit];//默认元素为 false
        Random r = new Random();
        while (arrLength > 0) {
            int x = r.nextInt(arrLimit);
            if(boo){
                v[--arrLength] = x;
            }else{
                if (arrLength > arrLimit) {
                    System.out.println("随机数取值范围不能小于数组长度");
                    break;
                }else{
                    if (!b[x]) {
                        b[x] = true;
                        v[--arrLength] = x;
                    }
                }
            }
        }
        return v;
    }
    /**
     * description: 数组排序
     * param : [arr：目标数组, type：排序类型]
     * create_user: hjibo
     * create_date: 2018-08-01
     * create_time: 09:51
     */
    public static void arrayOfSort(int[] arr,int sortType){
        switch (sortType){
            case 1:
                int[] temp = new int[arr.length];//临时数组
                mergeSort(arr,0,arr.length-1,temp);//归并排序
                temp = null;
                break;
            case 2:
                bubbleSort(arr);//冒泡排序
                break;
            case 3:
                quickSort(arr,0,arr.length-1);//快速排序
                break;
            case 4:
                insertSort(arr);//插入排序
                break;
            case 5:
                binaryInsertSort(arr);//二分插入排序
                break;
        }
    }
    /**
     * 插入排序
     * 原理：假设第一个元素为有序的，从第二个元素开始（待插入元素），依次与前面的元素从后往前比较，如果前面的元素大则将其后移一位，直到前面元素小于等于待插入元素为止，然后将元素插入当前位置，完成一次排序
     */
    private static void insertSort(int[] arr) {
        int i, j;
        int target;
        for (i = 1; i < arr.length; i++) { //以第一个元素为基数，从第二个元素开始排序
            j = i;
            target = arr[i];//待插入的目标元素
            while (j > 0 && arr[j - 1] > target) {
                arr[j] = arr[j - 1];
                j--;
            }
            arr[j] = target;
        }
    }
    /**
     * 二分插入排序
     * 原理：利用二分法确定待插入元素需要插入的位置，确定后先将大于待插入元素的数依次后移一位，然后将待插入元素插入指定位置
     */
    private static void binaryInsertSort(int[] arr) {
        int i, j;
        int low, high, mid;
        int temp;
        for (i = 1; i < arr.length; i++) {
            temp = arr[i];
            low = 0;
            high = i - 1;
            while (low <= high) {
                mid = (low + high) / 2;
                if (arr[mid] > temp)
                    high = mid - 1;
                else
                    low = mid + 1;
            }
            for (j = i - 1; j > high; j--)
                arr[j + 1] = arr[j];
            arr[high + 1] = temp;
        }
    }
    /**
     * 冒泡排序
     * 原理：从前往后，依次比较相邻两个数大小，如果前面的数大于后面的数就交换他们的位置，即小的数往前走，大的数往后走
     */
    private static void bubbleSort(int[] arr) {
        int temp;
        boolean flag;//标志位：循环开始时置为true，如果循环中发生数据交换（有排序行为），则置为false；如果没有发生数据交换，则跳出循环
        for (int i = 0; i < arr.length - 1; i++) {
            flag = true;
            for (int j = 0; j < arr.length - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                    flag = false;
                }
            }
            if (flag) {//没有发生交换(表示已经是有序的了)则退出循环；
                break;
            }
        }
    }
    /**
     * 快速排序
     * 原理：用三数取中法选择一个合适的数作为基数，然后先从后往前查找，直到找到比基数小的数（位置j）停止，再从前往后查找，直到找到比基数大的数位置i停止，然后交换arr[i]和arr[j]的位置
     *       直到 i = j时，交换arr[i]和基数的位置，此时完成一次比较，保证基数左边的数都比基数小，右边的数都比基数大；
     *       然后分别递归左边数组和右边数组
     */
    private static void quickSort(int[] arr, int L, int R) {
        if (L > R) {//当左边大于右边时，退出迭代
            return;
        }
        int i = L;
        int j = R;
        int temp;

        int middle = L + (R - L)/2; //三数取中法,将最大的数放到右边，将最小的数放到中间，将中间的数放到第一位作为基数
        if (arr[L] > arr[R]) {
            temp = arr[L];
            arr[L] = arr[R];
            arr[R] = temp;
        }
        if (arr[middle] > arr[R]) {
            temp = arr[middle];
            arr[middle] = arr[R];
            arr[R] = temp;
        }
        if (arr[L] < arr[middle]) {
            temp = arr[L];
            arr[L] = arr[middle];
            arr[middle] = temp;
        }
        int base = arr[L];//默认选取数组第一个元素作为基数

        while (i < j) {
            while (i < j && arr[j] >= base ) {//先从后往前查找，直到找到比基数小的数停止
                j--;
            }
            while (i < j  && arr[i] <= base) {//再从前往后查找，直到找到比基数大的数停止
                i++;
            }
            if (i < j) {//如果此时 i<j，则交换arr[i] 和 arr[j] 的位置
                temp = arr[j];
                arr[j] = arr[i];
                arr[i] = temp;
            }
        }

        //如果此时 i=j，则交换arr[i] 和 基数 的位置
        arr[L] = arr[i];
        arr[i] = base;

        //以当前基数的位置将数组分治
        quickSort(arr, L, i - 1);//递归左半数组
        quickSort(arr, i + 1, R);//递归右半数组
    }

    /**
     * 归并排序
     * 原理：首先创建一个与待排序数组大小相等的临时数组，用来存放合并后的序列；
     *       采用分治法
     */
    private static void mergeSort(int[] arr, int left, int right, int[] temp) {
        if (left < right) {
            int mid = (left + right) / 2;
            mergeSort(arr, left, mid, temp);//左边归并排序，使得左子序列有序
            mergeSort(arr, mid + 1, right, temp);//右边归并排序，使得右子序列有序
            merge(arr, left, mid, right, temp);//将两个有序子数组合并操作
        }
    }
    private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
        int i = left;//左序列指针
        int j = mid + 1;//右序列指针
        int t = 0;//临时数组指针
        while (i <= mid && j <= right) {
            if (arr[i] <= arr[j]) {
                temp[t++] = arr[i++];
            } else {
                temp[t++] = arr[j++];
            }
        }
        while (i <= mid) {//将左边剩余元素填充进temp中
            temp[t++] = arr[i++];
        }
        while (j <= right) {//将右序列剩余元素填充进temp中
            temp[t++] = arr[j++];
        }
        t = 0;
        //将temp中的元素全部拷贝到原数组中
        while (left <= right) {
            arr[left++] = temp[t++];
        }
    }

    /**
     * description: 数组查找
     * param : [arr, searchType]
     * create_user: hjibo
     * create_date: 2018-08-03
     * create_time: 15:17
     */
    public static int arrayOfSearch(int[] arr, int key, int searchType) {
        int result = -1;
        switch (searchType) {
            case 1:
                result = binarySearch(arr, 0, arr.length, key);//递归法查找
                break;
            case 2:
                result = binarySerach(arr, key);//非递归法查找
                break;
            case 3:
                result = binarySerachOfFirstLarge(arr, key);//查找第一个大于目标元素的索引位置
                break;
            default:
                return -1;
        }
        return result;
    }

    private static int binarySearch(int arr[], int start, int end, int key) {
        int mid = (end - start) / 2 + start;
        if (arr[mid] == key) {
            return mid;
        }
        if (start >= end) {
            return -1;
        } else if (key > arr[mid]) {
            return binarySearch(arr, mid + 1, end, key);
        } else if (key < arr[mid]) {
            return binarySearch(arr, start, mid - 1, key);
        }
        return -1;
    }

    private static int binarySerach(int[] arr, int key) {
        int left = 0;
        int right = arr.length - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (arr[mid] == key) {
                return mid;
            } else if (arr[mid] < key) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }
    private static int binarySerachOfFirstLarge(int[] array, int key) {
        int left = 0;
        int right = array.length - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (array[mid] <= key) {
                left = mid + 1;
            }
            else {
                right = mid - 1;
            }
        }
        return left;
    }

    /***************************************************** List 深度拷贝 操作*****************************************************/
    /**
     * description:  List 深度拷贝(修改附件list的元素值不会对原list有影响)
     * create_user: hjibo
     * create_date: 2018-09-12 19:04
     * param : [src]
     */
    public static <T> List<T> listDeepCopy(List<T> src) throws IOException, ClassNotFoundException {

        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(src);

        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        @SuppressWarnings("unchecked")
        List<T> dest = (List<T>) in.readObject();
        return dest;
    }

    /*****************************************************待。。。 操作*****************************************************/


}
